/**
  ETFAna project, Anyang Normal University && IMP-CAS
  \class ETFUnpackTask
  \brief abstract base class for tasks in the unpacking stage
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2021-10-04
  \date 2022-04-27 last modified
  \attention
  changelog
  <table>
  <tr>  <th>Date         <th>Author      <th>Description                   </tr>
  <tr>  <td>2021-10-04   <td>Asia Sun    <td>file created                  </tr>
  </table>

  \copyright Copyright (c) 2021-2024 Anyang Normal U. && IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <TFile.h>
#include <TDirectory.h>
#include <TH1F.h>
#include <TH1D.h>
#include <TH2F.h>
#include <TClonesArray.h>
#include "ETFUnpackTask.h"
#include "ETFRun.h"
#include "ETFMsg.h"
#include "ETFPXISection.h"
#include "ETFPXIFragment.h"
#include "ETFVMEEvent.h"
#include "ETFVMERawEvent.h"
#include "ETFHPTDCCh.h"
#include "ETFAmpCh.h"
#include "ETFParaManager.h"
#include "ETFSource.h"

using std::cout;
using std::flush;

#define er ETFMsg::Error

ETFUnpackTask::ETFUnpackTask(const char *name) : ETFTask(name), fRTSca{0}{}

ETFUnpackTask::~ETFUnpackTask(){}

void ETFUnpackTask::CreateHistos(){
  if(!(fFolder = dynamic_cast<TDirectory *>(fRootFile->FindObject("UnpackTask"))))
    fFolder = fRootFile->mkdir("UnpackTask", "Statistics for the Unpacking Stage");
  if(!fFolder) er("ETFUnpackTask", "CreateHistos: fFolder nullptr");

  hSecLenPXI = new TH1F("hSecLenPXI", "PXI Section Length;words", 5001, -0.5, 5000.5);
  hSecLenVME = new TH1F("hSecLenVME", "VME Event Length;words", 1001, -0.5, 1000.5);
  h2SecLenPXI = new TH2F("h2SecLenPXI", "PXI Section Length - unpacker id;id;words",
    7, -1.5, 5.5, 5001, -0.5, 5000.5);
  h2SecLenVME = new TH2F("h2SecLenVME", "VME Event Length - unpacker id;id;words",
    7, -1.5, 5.5, 1001, -0.5, 1000.5);
  hFragLen = new TH2F("hFragLen", "Fragment Data Length;start channel",
    5001, -0.5, 5000.5, 101, -0.5, 200.5);
  hCh = new TH1F("hCh", "Signal Channel Distribution among Channel IDs",
    5001, -0.5, 5000.5);
  hChv = new TH1F("hChv", "Channel Distribution - only Very High Mode",
    5001, -0.5, 5000.5);
  haCh = new TH1F("haCh", "Amp Channel Distribution", 301, 3699.5, 4000.5);
  htot = new TH2F("htot", "TOT v.s. ChId - 1st pulse;chId;TOT",
    5001, -0.5, 5000.5, 100, -10, 2000.5);
  htotV = new TH2F("htotV", "TOT (Very High) v.s. ChId - 1st pulse;chId;TOT",
    5001, -0.5, 5000.5, 100, -10, 2000.5);
  hnlvsChid = new TH2F("hnlvsChid", "Number of Leading Edges v.s. Channel Id",
    5001, -0.5, 5000.5, 15, -1.5, 13.5);
  hntvsChid = new TH2F("hntvsChid", "Number of Trailing Edges v.s. Channel Id",
    5001, -0.5, 5000.5, 15, -1.5, 13.5);
  hnl_ntvsChid = new TH2F("hnl_ntvsChid", "N-Leading_Edge - N-Trailing-Edge v.s. "
"Channel Id", 5001, -0.5, 5000.5, 17, -8.5, 8.5);
  // for amplitude channels //
  ha = new TH1F("ha", "Amplitude Distribution (QDC/ADC)", 500, -1., 5000.);
  hac = new TH2F("hac", "Amplitude Distribution v.s. Channel Id",
    201, 3699.5, 3900.5, 500, -1., 5000.);
  hqdc = new TH2F("hqdc", "QDC Amp Distribution v.s. Channel Id",
    201, -0.5, 200.5, 500, -1., 5000.);
  hRTsca = new TH1D("hRTsca", "Real Time (Dynamic) Scaler", 35, -0.25, 17.25);
  hRTsca->SetDrawOption("text");
  hsca = new TH1D("hsca", "v830 Scaler Output - accumulative", 35, -0.25, 17.25);
  hdsca = new TH1D("hdsca", "v830 Scaler Output - differential", 35, -0.25, 17.25);
  hdIndex = new TH1F("hdIndex", "pxi index - vme index", 200001, -100000.5, 100000.5);
  hpxiMonitor = new TH1F("hpxiMonitor", "PXI Event Index Monitor", 3000, 0, 100000000);
  hvmeMonitor = new TH1F("hvmeMonitor", "VME Event Index Monitor", 3000, 0, 100000000);

  // time to trig histos //
  hTimeToTrigCE[0] = new TH2F("hTimeToTrigC0", "Time To Trigger v.s. ChId-1st \
Pulse;channelId;timeToTrig [ns]", 5001, -0.5, 5000.5, 100, -500., 5000.);
  hTimeToTrigCE[1] = new TH2F("hTimeToTrigC1", "Time To Trigger v.s. ChId-2nd \
Pulse;channelId;timeToTrig [ns]", 5001, -0.5, 5000.5, 100, -500., 5000.);
  hTimeToTrigCE[2] = new TH2F("hTimeToTrigC2", "Time To Trigger v.s. ChId-3rd \
Pulse;channelId;timeToTrig [ns]", 5001, -0.5, 5000.5, 100, -500., 5000.);
  hTimeToTrigCE[3] = new TH2F("hTimeToTrigC3", "Time To Trigger v.s. ChId-4th \
Pulse;channelId;timeToTrig [ns]", 5001, -0.5, 5000.5, 100, -500., 5000.);
  hTimeToTrigC = new TH2F("hTimeToTrigC", "Time To Trigger v.s. ChId-All \
Pulses;channelId;timeToTrig [ns]", 5001, -0.5, 5000.5, 100, -500., 5000.);
  hTimeToTrigCV = new TH2F("hTimeToTrigCV", "Time To Trigger (Very High) v.s. ChId-All \
Pulses;channelId;timeToTrig [ns]", 5001, -0.5, 5000.5, 100, -500., 5000.);
  hTimeToTrigE = new TH2F("hTimeToTrigE", "Time To Trigger v.s. # Leading Edge;\
# leading edge;timeToTrig [ns]", 11, -0.5, 10.5, 100, -500., 5000.);
  hTimeToTrig = new TH1F("hTimeToTrig", "Time To Trigger - All Edges;timeToTrig [ns]",
    500, -500., 5000.);

  addh(hSecLenPXI, "daq"); addh(hSecLenVME, "daq"); addh(h2SecLenPXI); addh(h2SecLenVME);
  addh(hFragLen);          addh(hCh, "daq");        addh(hChv, "daq"); addh(haCh, "daq");
  addh(ha, "daq");         addh(hac, "daq");        addh(hqdc, "daq"); addh(htot, "daq");
  addh(htotV, "daq");      addh(hdIndex, "daq");
  addh(hRTsca, "daq");     addh(hsca, "daq");       addh(hdsca, "daq");
  addh(hnlvsChid); addh(hnl_ntvsChid); addh(hntvsChid);
  addh(hpxiMonitor, "daq"); addh(hvmeMonitor, "daq");
  // add time to trig histos //
  for(int i = 4; i--;) addh(hTimeToTrigCE[i], "daq");
  addh(hTimeToTrig, "daq");
  // also add to online GUI - daq tab //
  addh(hTimeToTrigC, "daq"); addh(hTimeToTrigCV, "daq"); addh(hTimeToTrigE, "daq");
} // end member function CreateHistos

void ETFUnpackTask::Exec(){
  // --------------------- PXI PART ------------------------------ //
  for(auto &p : fPXISections){
    const int uid = p->id(), len = p->GetSectionLength();
    hSecLenPXI->Fill(len);
    h2SecLenPXI->Fill(uid, len);
    for(TObject *fragObj : *p->GetFragments()){
      ETFPXIFragment *frag = dynamic_cast<ETFPXIFragment *>(fragObj);
      if(!frag) er("ETFUnpackTask", "Exec: null fragment encountered");
      const fragment_head &frag_h = frag->GetFragmentHead();
      hFragLen->Fill(frag_h.start_channel, frag_h.data_len);
    } // end for over fragments
    // ================ read HPTDC channles ====================== //
    TClonesArray *tchs = p->GetHPTDCChannels();
    // if(p->UnpackGood())
    for(TObject *tchObj : *tchs){
      ETFHPTDCCh *tch = dynamic_cast<ETFHPTDCCh *>(tchObj);
      if(!tch) er("ETFUnpackTask", "Exec: null PXI HPTDC channel encountered");
      const int chid = tch->GetChannelId();
      const int nl = tch->GetNofLeadingEdges(), nt = tch->GetNofTrailingEdges();
      const bool isv = tch->IsV();

      hCh->Fill(chid);
      hnl_ntvsChid->Fill(chid, nl - nt);
      hnlvsChid->Fill(chid, nl); hntvsChid->Fill(chid, nt);
      if(isv) hChv->Fill(chid);
      if(nl > 0 && nt > 0){
        const double tot = tch->tot();
        htot->Fill(chid, tot);
        if(isv) htotV->Fill(chid, tot);
      } // end if
      // time to trig //
      for(int i = 0; i < nl; i++){
        const double t = tch->GetLeadingEdge(i);
        if(i <= 3) hTimeToTrigCE[i]->Fill(chid, t);
        hTimeToTrigE->Fill(i, t); hTimeToTrig->Fill(t);
        hTimeToTrigC->Fill(chid, t);
        if(isv) hTimeToTrigCV->Fill(chid, t);
      } // end inner for
    } // end for over HPTDC channels
  } // end for over PXI Sections
  // --------------------- VME PART ------------------------------ //
  for(auto &v : fVMEEvents){
    const int uid = v->id(), len = v->GetEventLength();
    hSecLenVME->Fill(len);
    h2SecLenVME->Fill(uid, len);
    for(TObject *tchObj : *v->GetHPTDCChannels()){
      ETFHPTDCCh *tch = dynamic_cast<ETFHPTDCCh *>(tchObj);
      if(!tch) er("ETFUnpackTask", "Exec: null VME HPTDC channel encountered");
      const int chid = tch->GetChannelId();
      const int nl = tch->GetNofLeadingEdges(), nt = tch->GetNofTrailingEdges();
      const bool isv = tch->IsV();

      hCh->Fill(chid); hnlvsChid->Fill(chid, nl); hntvsChid->Fill(chid, nt);
      hnl_ntvsChid->Fill(chid, nl - nt);
      if(nl > 0 && nt > 0){
        const double tot = tch->tot();
        htot->Fill(chid, tot);
        if(isv) htot->Fill(chid, tot);
      } // end if
      for(int i = 0; i < nl; i++){
        const double t = tch->GetLeadingEdge(i);
        if(i <= 3) hTimeToTrigCE[i]->Fill(chid, t);
        hTimeToTrigE->Fill(i, t); hTimeToTrig->Fill(t);
        hTimeToTrigC->Fill(chid, t);
        if(isv) hTimeToTrigCV->Fill(chid, t);
      } // end inner for
    } // end for over HPTDC channels
    for(TObject *achObj : *v->GetAmplitudeChannels()){
      ETFAmpCh *ach = dynamic_cast<ETFAmpCh *>(achObj);
      if(!ach) er("ETFUnpackTask", "Exec: null VME amplitude channel encountered");
      const int chid = ach->chid();
      const double a = ach->value();
      haCh->Fill(chid); // amplitude channel: bound to be fired for any event
      ha->Fill(a);
      hac->Fill(chid, a);
    } // end for over amp channels
    // take care of raw qdc (dual channel ones) data
    ETFVMERawEvent *rv = v->raw();
    const auto &qdc = rv->qdc();
    int chid0 = 0;
    for(const auto &c : qdc){
      const int nq = c->size(); // nof qdc channels per card
      for(int i = 0; i < nq; i++) hqdc->Fill(chid0 + i, (*c)[i]);
      chid0 += nq;
    } // end for over qdc cards
    // set scalers //
    for(int i = 0; i < 16; i++){
      // if(7 == i) continue;
      const int j = 2*i + 1;
      const int dsca = v->dsca(i), sca = v->sca(i);
      hsca->SetBinContent(j, sca);
      hdsca->SetBinContent(j, dsca);
      if(fpm->IsReset() && 0 == i){ // so that hRTsca clears and counts from
        for(auto &t : fRTSca) t = 0; // the start
        // fRTSca[1] = fRTSca[6] = -10003; // to correct scaler malfunction
        hRTsca->Reset();
        fpm->RecoverReset();
        ETFMsg::Info("ETFUnpackTask", "Exec: Reset RTsca");
      } // end if
      fRTSca[i] += dsca;
      // hRTsca->SetBinContent(j, hRTsca->GetBinContent(j) + dsca);
      hRTsca->SetBinContent(j, fRTSca[i]);
    } // end for
  } // end for over vme events
  // event index monitor //
  if(fPXISections.size()) hpxiMonitor->Fill(fPXISections[0]->Index());
  if(fVMEEvents.size()) hvmeMonitor->Fill(fVMEEvents[0]->Index());
  if(fPXISections.size() && fVMEEvents.size())
    hdIndex->Fill(fPXISections[0]->Index() - fVMEEvents[0]->Index());
} // end member function Exec
